﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 喜马拉雅音频下载器
{
    public partial class Form1 : Form
    {
        static int DownLoadCount = 0;
        static int DownLoadCounted = 0;
        static DateTime[] TimeSpans;
        static long[] LastSize;
        int ConnectionLimit = 10;

        public Form1()
        {
            InitializeComponent();
        }

        #region 分析添加页面数据
        private void button1_Click(object sender, EventArgs e)
        {
            ServicePointManager.DefaultConnectionLimit = ConnectionLimit;

            if (this.txtUrl.Text.Trim() == "") return;

            this.dataGridView1.Rows.Clear();
            if (!this.isConnected())
            {
                MessageBox.Show("未连接网络!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Hand);
            }
            else
            {
                //this.showinfo("正在获取...");
                if (this.txtUrl.Text.Contains("/sound/"))
                {
                    string text = this.txtUrl.Text;
                    string DocID = text.Substring(text.IndexOf("sound/") + 6);
                    Object[] values = new Object[] {DocID};
                    int num = this.dataGridView1.Rows.Add(values);
                }
                else
                {
                    byte[] bytes = new WebClient().DownloadData(this.txtUrl.Text);
                    string input = Encoding.UTF8.GetString(bytes);
                    string str4 = Regex.Match(input, @"<h1>([\w\W]*?)</h1>").Groups[1].Value;
                    string pattern = "(?i)<li sound_id=\"(\\d*)\">[\\w\\W]*?hashlink[\\w\\W]*?>([\\w\\W]*?)</a>";

                    String temp= "";

                    foreach (Match match in Regex.Matches(input, pattern, RegexOptions.Multiline | RegexOptions.IgnoreCase))
                    {
                        String DocID = match.Groups[1].Value.TrimStart().TrimEnd();
                        temp = DocID;
                        Object[] strArray2 = new Object[] 
                        {
                            //DocID
                            DocID,
                            //DocUrl
                            "",
                            //DownLoad
                            true,
                            //专辑
                            GetIllegalFileName(ToSimplified(str4.TrimStart().TrimEnd())),
                            //Album,
                            //声音
                            GetIllegalFileName(ToSimplified(match.Groups[2].Value.TrimStart().TrimEnd()))
                            //DocName,
                            //duration
                            //duration
                        };
                        int num2 = this.dataGridView1.Rows.Add(strArray2);
                    }
                }

                this.dataGridView1.Columns["Album"].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
                this.dataGridView1.Columns["Album"].Resizable = DataGridViewTriState.True;

                this.dataGridView1.Columns["DocName"].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
                this.dataGridView1.Columns["DocName"].Resizable = DataGridViewTriState.True;
                this.dataGridView1.ColumnHeadersDefaultCellStyle.Alignment= DataGridViewContentAlignment.MiddleCenter;

                this.dataGridView1.Columns["duration"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
                this.dataGridView1.Columns["FileSize"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
                this.dataGridView1.Columns["SynSpeed"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
                this.dataGridView1.Columns["SynProgress"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;

                this.button2.Enabled = true;
                UpdateDataGridInfoA();
            }
        }
        private void UpdateDataGridInfoA()
        {
            for (int i = 0; i < dataGridView1.Rows.Count; i++)
            {
                ParameterizedThreadStart ParStart = new ParameterizedThreadStart(UpdateDataGridInfo);
                Thread myThread = new Thread(ParStart);
                object o = dataGridView1.Rows[i].Cells["DocID"].Value;
                myThread.Start(o);
            }
        }

        public void UpdateDataGridInfo(Object DocID)
        {
            JToken token = JsonConvert.DeserializeObject<JToken>(this.HttpGet("http://www.ximalaya.com/tracks/" + DocID + ".json"));

            String DocUrl = token["play_path_64"].ToString();
            if (DocUrl.IndexOf("http://") < 0)
            {
                DocUrl = "http://fdfs.xmcdn.com/" + token["play_path_64"].ToString();
            }

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(DocUrl);

            request.MaximumAutomaticRedirections = 4;
            request.MaximumResponseHeadersLength = 4;
            request.Credentials = CredentialCache.DefaultCredentials;
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            long FileSize = response.ContentLength;
            response.Close();


            String DocName = this.FixFileName(token["title"].ToString());
            String Album = this.FixFileName(token["album_title"].ToString());
            int duration = (int)Convert.ToDouble(token["duration"].ToString());

            this.Invoke(new InvokeHandler(delegate ()
            {
                for (int i = 0; i < dataGridView1.Rows.Count; i++)
                {
                    if (DocID.ToString() == dataGridView1.Rows[i].Cells["DocID"].Value.ToString())
                    {
                        dataGridView1.Rows[i].Cells["DocUrl"].Value = DocUrl;
                        dataGridView1.Rows[i].Cells["duration"].Value = duration / 60 + "分钟";
                        dataGridView1.Rows[i].Cells["FileSize"].Value = FileOperate.GetAutoSizeString(FileSize, 2);
                        dataGridView1.Rows[i].Cells["FileSizeL"].Value = FileSize;
                        dataGridView1.Rows[i].Cells["DownLoad"].Value = true;

                        if (dataGridView1.Rows[i].Cells["Album"].Value == null) dataGridView1.Rows[i].Cells["Album"].Value = GetIllegalFileName(ToSimplified(Album).Trim());
                        if (dataGridView1.Rows[i].Cells["DocName"].Value == null) dataGridView1.Rows[i].Cells["DocName"].Value = GetIllegalFileName(ToSimplified(DocName).Trim());
                    }
                }
            }));

            
        }
        private String GetIllegalFileName(String FileName )
        {
            FileName = System.Web.HttpUtility.HtmlDecode(FileName);

            string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
            FileName = FileName.Replace(":", "：");
            FileName = FileName.Replace(",", "，");

            foreach (char c in invalid)
            {
                FileName = FileName.Replace(c.ToString(), "");
            }

            return FileName;
        }
        private delegate void InvokeHandler();
        #endregion

        private void AddGridViewColumns(DataGridView dgv)
        {
            DataGridViewTextBoxColumn dataGridViewColumn = new DataGridViewTextBoxColumn
            {
                DataPropertyName = "DocID",
                HeaderText = "文件ID",
                Visible = false,
                Name = "DocID",
                Width = 0,
                SortMode = DataGridViewColumnSortMode.NotSortable
            };
            dgv.Columns.Add(dataGridViewColumn);

            DataGridViewTextBoxColumn column0 = new DataGridViewTextBoxColumn
            {
                DataPropertyName = "DocUrl",
                HeaderText = "音乐Url",
                Visible = false,
                Name = "DocUrl",
                Width = 0,
                SortMode = DataGridViewColumnSortMode.NotSortable
            };
            dgv.Columns.Add(column0);

            DataGridViewCheckBoxColumn column1 = new DataGridViewCheckBoxColumn
            {
                AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
                DataPropertyName = "DownLoad",
                HeaderText = "下载",
                Name = "DownLoad",
                Width = 50,
                ReadOnly=false,
                SortMode = DataGridViewColumnSortMode.NotSortable
            };
            dgv.Columns.Add(column1);

            DataGridViewTextBoxColumn column2 = new DataGridViewTextBoxColumn
            {
                //AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells,
                DataPropertyName = "Album",
                HeaderText = "专辑",
                Name = "Album",
                Width = 120,
                SortMode = DataGridViewColumnSortMode.NotSortable
            };
            dgv.Columns.Add(column2);

            DataGridViewTextBoxColumn column3 = new DataGridViewTextBoxColumn
            {
                //AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells,
                DataPropertyName = "DocName",
                HeaderText = "音频",
                Name = "DocName",
                Width = 120,
                SortMode = DataGridViewColumnSortMode.NotSortable
            };
            dgv.Columns.Add(column3);

            DataGridViewTextBoxColumn column4 = new DataGridViewTextBoxColumn
            {
                AutoSizeMode = DataGridViewAutoSizeColumnMode.None,
                DataPropertyName = "duration",
                HeaderText = "时长",
                Name = "duration",
                Width = 90,
                SortMode = DataGridViewColumnSortMode.NotSortable
            };
            dgv.Columns.Add(column4);

            DataGridViewTextBoxColumn column5 = new DataGridViewTextBoxColumn
            {
                DataPropertyName = "FileSize",
                HeaderText = "大小",
                Name = "FileSize",
                SortMode = DataGridViewColumnSortMode.NotSortable
            };
            dgv.Columns.Add(column5);

            DataGridViewTextBoxColumn column51 = new DataGridViewTextBoxColumn
            {
                DataPropertyName = "FileSizeL",
                HeaderText = "大小",
                Name = "FileSizeL",
                Visible=false,
                SortMode = DataGridViewColumnSortMode.NotSortable
            };
            dgv.Columns.Add(column51);

            DataGridViewTextBoxColumn column6 = new DataGridViewTextBoxColumn
            {
                DataPropertyName = "SynSpeed",
                HeaderText = "速度",
                Name = "SynSpeed",
                SortMode = DataGridViewColumnSortMode.NotSortable
            };
            dgv.Columns.Add(column6);

            DataGridViewTextBoxColumn column7 = new DataGridViewTextBoxColumn
            {
                DataPropertyName = "SynProgress",
                HeaderText = "进度",
                Name = "SynProgress",
                SortMode = DataGridViewColumnSortMode.NotSortable
            };
            dgv.Columns.Add(column7);

            DataGridViewTextBoxColumn column8 = new DataGridViewTextBoxColumn
            {
                DataPropertyName = "FileName",
                HeaderText = "本地文件名",
                Name = "FileName",
                Visible = false,
                SortMode = DataGridViewColumnSortMode.NotSortable
            };
            dgv.Columns.Add(column8);

            DataGridViewCellStyle style = new DataGridViewCellStyle();
            style.Font = new Font("微软雅黑", 10, FontStyle.Bold); 
            //style.ForeColor = Color.Red;
            foreach (DataGridViewColumn col in this.dataGridView1.Columns)
            {
                col.HeaderCell.Style = style;
            }
            this.dataGridView1.EnableHeadersVisualStyles = false;
        }


        private void button2_Click(object sender, EventArgs e)
        {
            //                        if (dataGridView1.Rows[i].Cells["FileName"].Value == null) dataGridView1.Rows[i].Cells["DocName"].Value = ToSimplified(DocName);
            //重名的文件重命名
            for (int i = 0; i < dataGridView1.Rows.Count; i++)
            {
                dataGridView1.Rows[i].Cells["FileName"].Value = dataGridView1.Rows[i].Cells["DocName"].Value.ToString().Trim();
            }

            for (int i = 0; i < dataGridView1.Rows.Count; i++)
            {
                int k = 0;
                for (int j = i+1; j < dataGridView1.Rows.Count; j++)
                {
                    if(dataGridView1.Rows[j].Cells["FileName"].Value.ToString() == dataGridView1.Rows[i].Cells["FileName"].Value.ToString())
                    {
                        k++;
                        dataGridView1.Rows[j].Cells["FileName"].Value = dataGridView1.Rows[i].Cells["FileName"].Value.ToString()+"("+k+")";
                    }
                }
            }

            DownLoadCount = 0;
            DownLoadCounted = 0;
            TimeSpans = new DateTime[dataGridView1.Rows.Count];
            LastSize=new long[dataGridView1.Rows.Count];

            //检查线程
            for (int i = 0; i < dataGridView1.Rows.Count; i++)
            {
                if (dataGridView1.Rows[i].Cells["duration"].Value ==null)
                {
                    MessageBox.Show("请稍后，程序未执行完毕！");
                    return;
                }
            }

            this.button2.Enabled = false;

            for (int i = 0; i < dataGridView1.Rows.Count; i++)
            {
                String DocID = dataGridView1.Rows[i].Cells["DocID"].Value.ToString();
                String Album = dataGridView1.Rows[i].Cells["Album"].Value.ToString();
                String DocName = dataGridView1.Rows[i].Cells["DocName"].Value.ToString();
                String DocUrl = dataGridView1.Rows[i].Cells["DocUrl"].Value.ToString();
                String duration = dataGridView1.Rows[i].Cells["duration"].Value.ToString();
                String FileSize = dataGridView1.Rows[i].Cells["FileSizeL"].Value.ToString();
                String FileName = dataGridView1.Rows[i].Cells["FileName"].Value.ToString();

                //跳过未选中的不下载，默认选择
                if (!(Boolean)dataGridView1.Rows[i].Cells["DownLoad"].Value) continue;

                //检查文件大小
                String FilePath = Path.Combine(GetIllegalFileName(Album), FileName + Path.GetExtension(DocUrl));
                if (File.Exists(FilePath))
                {
                    if ((new FileInfo(FilePath)).Length.ToString() == FileSize)
                    {
                        dataGridView1.Rows[i].Cells["SynProgress"].Value = "100%";
                        continue;
                    }
                }
                //下载文件
                DownLoadFile(i, DocUrl, FilePath);
                TimeSpans[i] = DateTime.Now;
                DownLoadCount++;
            }

        }

        /// <summary>
        /// 繁体字转换为简体字
        /// </summary>
        /// <param name="source">繁体字字符串</param>
        /// <returns>简体字字符串</returns>
        public static string ToSimplified(string source)
        {
            return Microsoft.VisualBasic.Strings.StrConv(source, Microsoft.VisualBasic.VbStrConv.SimplifiedChinese, 0);
        }

        private void DownLoadFile(int i, String DocUrl, String SavePath)
        {
            String dir = Path.GetDirectoryName(SavePath);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }

            if (File.Exists(SavePath)) File.Delete(SavePath);

            try
            {
                WebClient client = new WebClient();
                client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(this.client_DownloadProgressChanged);
                client.DownloadFileCompleted += new AsyncCompletedEventHandler(this.client_DownloadFileCompleted);
                client.DownloadFileAsync(new Uri(DocUrl), SavePath, i);
            }
            catch(Exception e)
            {
                MessageBox.Show(e.Message);
            }

        }

        private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            DownLoadCounted++;
            dataGridView1.Rows[(int)e.UserState].Cells["SynProgress"].Value = "100%";

            if (DownLoadCounted>= DownLoadCount)
            {
                //MessageBox.Show("下载完毕");
                this.button2.Enabled = true;
                this.Focus();
                this.Activate();

            }
        }

        private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            try
            {
                int i = (int)e.UserState;

                this.Invoke(new InvokeHandler(delegate ()
                {
                    dataGridView1.Rows[i].Cells["SynProgress"].Value = e.ProgressPercentage + "%";
                }));

                if (LastSize[i] == 0)
                {
                    LastSize[i] = e.BytesReceived;
                    TimeSpans[i] = DateTime.Now;
                    return;
                }

                TimeSpan span = (TimeSpan)(DateTime.Now - TimeSpans[i]);
                double totalSeconds = span.TotalSeconds;

                this.Invoke(new InvokeHandler(delegate ()
                {
                    dataGridView1.Rows[i].Cells["SynSpeed"].Value = FileOperate.GetAutoSizeString(Convert.ToDouble((double)(((double)e.BytesReceived) / totalSeconds)), 2) + "/s";
                }));
            }
            catch(Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
            
        }
        private string FixFileName(string s)
        {
            char[] chArray = "\\/:*?\"<>|'".ToCharArray();
            char newChar = '_';
            foreach (char ch2 in chArray)
            {
                s = s.Replace(ch2, newChar);
            }
            return s;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.AddGridViewColumns(this.dataGridView1);
        }

 
        private string HttpGet(string url)
        {
            byte[] bytes = new WebClient().DownloadData(url);
            return Encoding.UTF8.GetString(bytes);
        }

        [DllImport("wininet.dll")]
        private static extern bool InternetGetConnectedState(out int connectionDescription, int reservedValue);

        private bool isConnected()
        {
            int connectionDescription = 0;
            return InternetGetConnectedState(out connectionDescription, 0);
        }
        
        private void txtUrl_TextChanged(object sender, EventArgs e)
        {
            this.txtUrl.Text = this.txtUrl.Text.Replace("/#/", "/");
        }

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex == -1) return;
            if (e.ColumnIndex == -1) return;

            if (e.RowIndex >= this.dataGridView1.Rows.Count) return;
            if (e.ColumnIndex >= this.dataGridView1.ColumnCount) return;

            if (this.dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].GetType() == typeof(DataGridViewCheckBoxCell))
            {
                this.dataGridView1.Rows[e.RowIndex].Cells["DownLoad"].Value = !(Boolean)(this.dataGridView1.Rows[e.RowIndex].Cells["DownLoad"].Value);
            }
        }

        private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            //Process ps = new Process();
            //string yourURL = this.dataGridView1.Rows[e.RowIndex].Cells["DownLoad"].ToString();
            //ps.StartInfo.FileName = "iexplore.exe";
            //ps.StartInfo.Arguments = yourURL;
            //ps.Start();
            String DocID= this.dataGridView1.Rows[e.RowIndex].Cells["DocID"].Value.ToString();
            JToken token = JsonConvert.DeserializeObject<JToken>(this.HttpGet("http://www.ximalaya.com/tracks/" + DocID + ".json"));


            String url = "http://www.ximalaya.com/"+ token["uid"].ToString().Trim() + "/sound/"+ DocID;
            System.Diagnostics.Process.Start(url);
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            System.Environment.Exit(0);
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start("http://www.ximalaya.com/");
        }

        private void dataGridView1_MouseHover(object sender, EventArgs e)
        {

        }
    }


    public class FileOperate
    {
        private const double GBCount = 1073741824.0;
        private const double KBCount = 1024.0;
        private const double MBCount = 1048576.0;
        private const double TBCount = 1099511627776;

        public static string GetAutoSizeString(double size, int roundCount)
        {
            if (1024.0 > size)
            {
                return (Math.Round(size, roundCount) + "B");
            }
            if (1048576.0 > size)
            {
                return (Math.Round((double)(size / 1024.0), roundCount) + "KB");
            }
            if (1073741824.0 > size)
            {
                return (Math.Round((double)(size / 1048576.0), roundCount) + "MB");
            }
            if (1099511627776 > size)
            {
                return (Math.Round((double)(size / 1073741824.0), roundCount) + "GB");
            }
            return (Math.Round((double)(size / 1099511627776), roundCount) + "TB");
        }
    }
}
